CSS Sprites is a great modern web design technique for
reducing the number of image server requests on a web page. There are a
lot of online resources and books available on this technique. For now,
suffice it to say that if you have many images in your site (preferred
logos, icons, background images, flags, etc.), you can reduce all of those
to one big image with all the originals inside and use a CSS mask to
determine which portion of it to show in each container.This technique has a great impact on web performance, but for mobile
applications, we should think twice before using it and analyze the
possible problems. First, we need full background-position CSS property compatibility
(the mobile standards include this, so it’s not really an issue). The
second consideration is that we will not be using img tags. In their place, we will use any block
element (div) or any block-converted
element using display: block, such as a
span or a tag. This means that we cannot provide
alternative text for the images, and the browser won’t know how much space
to allocate for each image until it renders the CSS file.
Finally, in some browsers this technique can have an impact on
rendering performance, because the big image will be duplicated in memory
for each usage. We need to balance the performance gained through the
reduction of requests with the performance lost in the rendering engine in
some browsers.
1. Samples and Compatibility
Let’s create a sample using two techniques: using an original
block element (div) and using an
original inline element (a) converted
to a block element.
The original document without CSS Sprites is the following country
list:
<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN"
"http://www.wapforum.org/DTD/xhtml-mobile10.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Documento sin título</title>
<style type="text/css">
ul {
list-style: circle;
}
ul li {
padding: 0px;
margin-bottom: 5px;
}
ul li img {
margin: 0px 10px 0px 0px;
vertical-align: middle;
border: 1px solid gray;
}
</style>
</head>
<body>
<h1>The Best Seller</h1>
<h2>Select your nearest country</h2>
<ul>
<li><img src='ar.png' width='30' height='19' alt='AR' />
<a href='ar'>Argentina</a></li>
<li><img src='br.png' width='30' height='19' alt='BR' />
<a href='br'>Brazil</a></li>
<li><img src='fi.png' width='30' height='19' alt='FI' />
<a href='fi'>Finland</a></li>
<li><img src='jp.png' width='30' height='19' alt='JP' />
<a href='jp'>Japan</a></li>
<li><img src='es.png' width='30' height='19' alt='ES' />
<a href='es'>Spain</a></li>
<li><img src='us.png' width='30' height='19' alt='US' />
<a href='us'>United States</a></li>
</ul>
</body>
</html>
The previous sample uses six images that can be converted into a
single one (saving five requests and the HTTP and PNG headers) with the
following code:<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN"
"http://www.wapforum.org/DTD/xhtml-mobile10.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Documento sin título</title>
<style type="text/css">
ul {
list-style: circle;
}
ul li {
padding: 0px;
margin-bottom: 5px;
}
ul li div {
/* We define the large image to all divs that represent an image*/
background:url(sprite.png);
width: 30px;
height: 19px;
float: left;
border: 1px solid gray;
margin-right: 10px;
}
</style>
</head>
<body>
<h1>The Best Seller</h1>
<h2>Select your nearest country</h2>
<ul>
<li><div style="background-position: 0px 0px;"></div>
<a href='ar'>Argentina</a></li>
<li><div style="background-position: 0px −29px;"></div>
<a href='br'>Brazil</a></li>
<li><div style="background-position: 0px −58px;"></div>
<a href='fi'>Finland</a></li>
<li><div style="background-position: 0px −87px;"></div>
<a href='jp'>Japan</a></li>
<li><div style="background-position: 0px −116px;"></div>
<a href='es'>Spain</a></li>
<li><div style="background-position: 0px −145px;"></div>
<a href='us'>United States</a></li>
</ul>
</body>
</html>
This produces the result shown in Figure 1.
Note:
There are plenty of online CSS Sprites generators where you can
upload all your images and receive in seconds one big image and the
CSS code to replace each of the original img tags. Examples include http://spritegen.website-performance.org and http://csssprites.com.
Now let’s look at applying the same technique to a non-original
block element, such as the a tag. The
only problem will be the flag border: as we use the same a tag for the image and the text, we cannot
define a border. The code looks like this:
<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN"
"http://www.wapforum.org/DTD/xhtml-mobile10.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Documento sin título</title>
<style type="text/css">
ul {
list-style: circle;
}
ul li {
padding: 0px;
margin-bottom: 5px;
}
ul li a {
/* We define the large image to all divs that represent an image */
background:url(sprite.png);
/* We need to create block elements */
display: block;
/* We need the background to not be repeated */
background-repeat: no-repeat;
height: 19px;
padding-left: 40px;
}
</style>
</head>
<body>
<h1>The Best Seller</h1>
<h2>Select your nearest country</h2>
<ul>
<li>
<a href='ar' style='background-position: 0px 0px;'>Argentina</a></li>
<li>
<a href='br' style='background-position: 0px −29px;'>Brazil</a></li>
<li>
<a href='fi' style='background-position: 0px −58px;'>Finland</a></li>
<li>
<a href='jp' style='background-position: 0px −87px;'>Japan</a></li>
<li>
<a href='es' style='background-position: 0px −116px;'>Spain</a></li>
<li>
<a href='us' style='background-position: 0px −145px;'>United States</a></li>
</ul>
</body>
</html>
Warning:Using CSS Sprites is not recommended for big files or photo
images. If you are using PNG images, the best way to approach it is to
group icons with a consistent color palette.
Table 1 lists
CSS Sprites compatibility for the various platforms.
Table 1. CSS Sprites compatibility table
Browser/platform | Sprites over
div | Sprites over
anchors |
---|
Safari | Yes | Yes |
Android
browser | Yes | Yes |
Symbian/S60 | Yes | Yes |
Nokia Series
40 | Yes in
6th edition No before
6th edition | Yes, buggy on low-end
devices |
webOS | Yes | Yes |
BlackBerry | Yes from
4.0 | Yes from
4.0 |
NetFront | No | Yes |
Openwave
(Myriad) | No | No |
Internet
Explorer | No | Yes |
Motorola Internet
Browser | No | No |
Opera
Mobile | Yes | Yes |
Opera Mini | Yes | Yes |
2. CSS Sprites Alternatives
The idea behind optimizing the number of requests to the
server is very interesting, even if you reject the usage of CSS Sprites.
That is why we need to think about alternatives to this technique for
some specific situations.
Warning:
Image maps are the first technique that comes to mind as
a CSS Sprites alternative. However, they are not recommended for
non-touch navigation, because image maps in non-touch devices can have
a negative impact on usability.
2.1. Inline images
Inline images are a
great technique for compatible browsers. When designing for browsers
that understand them, we can copy the first sample (the original
document without CSS Sprites) and replace the URL of each image with
the data: representation.
2.2. Join images
If the images are near one another horizontally or
vertically, as in our sample, we can consider joining all the images
into one. The concept is similar to CSS Sprites, but we set up the
image as a single-use background, adjusting the margins and padding so
that the elements are properly aligned with the different parts of the
image. This technique can have poor results on old devices with
limited support for margins and padding.
Note:
If we use the original code but define a good cache policy on
the server, subsequent pages of the site will load faster than if we
used CSS Sprites, because no rendering work will be required.
2.3. Box borders
If you were thinking of using CSS Sprites to define the
borders of a rectangular area, there is a WebKit extension that can
help you. In the following section, we will get deeper into
this.